<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务，帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2024 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件，不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);

namespace app\store\model\goods;

use think\Paginator;
use think\db\exception\DbException;
use PhpOffice\PhpSpreadsheet\Exception;
use app\job\controller\goods\Import as GoodsImportJob;
use app\common\enum\goods\ImportStatus as GoodsImportStatusEnum;
use app\common\library\FileLocal;
use app\common\library\helper;
use app\common\library\phpoffice\ReadExecl;
use app\common\model\goods\Import as ImportModel;
use cores\exception\BaseException;

/**
 * 商品批量导入记录模型
 * Class Import
 * @package app\store\model\goods
 */
class Import extends ImportModel
{
    /**
     * 获取导出记录
     * @param array $param
     * @return Paginator
     * @throws DbException
     */
    public function getList(array $param = []): \think\Paginator
    {
        return $this->where($this->getFilter($param))
            ->where('is_delete', '=', 0)
            ->order(['create_time' => 'desc', $this->getPk()])
            ->paginate();
    }

    /**
     * 获取查询条件
     * @param array $param
     * @return array
     */
    private function getFilter(array $param = []): array
    {
        // 默认查询参数
        $params = $this->setQueryDefaultValue($param, ['goods_type' => -1, 'status' => -1]);
        // 检索查询条件
        $filter = [];
        $params['status'] > -1 && $filter[] = ['status', '=', (int)$params['status']];
        return $filter;
    }

    /**
     * 执行批量导入
     * @param array $form
     * @return bool
     * @throws BaseException
     * @throws Exception
     * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
     */
    public function batch(array $form): bool
    {
        // 读取excel文件内容
        $execlData = $this->readExecl();
        // 验证导入的商品数量是否合法
        $this->checkLimit($execlData);
        // 格式化导入的商品列表数据
        $goodsList = $this->formatGoodsList($execlData);
        // 新增商品导入记录
        $recordId = $this->addRecord(\count($goodsList));
        // 调度计划任务
        $this->dispatchJob($goodsList, $recordId);
        return true;
    }

    /**
     * 调度队列服务执行商品导入
     * @param array $goodsList 商品列表
     * @param int $recordId 商品导入记录ID
     * @return void
     */
    private function dispatchJob(array $goodsList, int $recordId)
    {
        // 分批每次导入20条
        $limit = 20;
        // 根据商品总数量计算需要的队列任务数量
        $jobCount = \count($goodsList) / $limit;
        // 逐次发布队列任务
        for ($i = 0; $i < $jobCount; $i++) {
            $data = array_slice($goodsList, $i * $limit, $limit);
            GoodsImportJob::dispatch([
                'list' => $data,
                'recordId' => $recordId,
                'storeId' => self::$storeId,
            ]);
        }
    }

    /**
     * 新增商品导入记录
     * @param int $totalCount 商品总数量
     * @return int
     */
    private function addRecord(int $totalCount): int
    {
        $this->save([
            'total_count' => $totalCount,
            'start_time' => \time(),
            'fail_log' => [],
            'status' => GoodsImportStatusEnum::NORMAL,
            'store_id' => self::$storeId
        ]);
        return (int)$this['id'];
    }

    /**
     * 格式化导入的商品列表数据
     * @param array $execlData
     * @return array
     */
    private function formatGoodsList(array $execlData): array
    {
        $goodsList = [];
        foreach ($execlData as $row) {
            // 不存在商品序号的记录视为空行跳过
            if (!is_numeric($row['A'])) {
                continue;
            }
            // SKU信息
            $skuInfo = helper::pick($row, ['G', 'H', 'I', 'J', 'K', 'L', 'M', 'N']);
            // 商品数据里不存在相同序号, 代表是第一次记录
            if (!isset($goodsList[$row['A']])) {
                $goodsList[$row['A']] = $row;
                // 存在规格值组合则商品是多规格
                if (!empty($row['H'])) {
                    $goodsList[$row['A']]['skuList'] = [$skuInfo];
                }
            } elseif (isset($goodsList[$row['A']]['skuList'])) {
                // 否则代表是多规格商品的SKU
                $goodsList[$row['A']]['skuList'][] = $skuInfo;
            }
        }
        return array_values($goodsList);
    }

    /**
     * 验证导入的商品数量是否合法
     * @param array $execlData
     * @return void
     * @throws BaseException
     */
    private function checkLimit(array $execlData): void
    {
        // 判断商品数据是否为空
        if (empty($execlData)) {
            throwError('很抱歉，模板文件中商品数量为空');
        }
        // 过滤重复的商品序号
        $originalUnique = helper::arrayUnique($execlData, 'A');
        // 判断商品数量是否超出500条
        if (\count($originalUnique) > 500) {
            throwError('很抱歉，模板文件中最多不能超过500个商品');
        }
    }

    /**
     * 读取excel文件内容
     * @return array
     * @throws BaseException
     * @throws \PhpOffice\PhpSpreadsheet\Exception
     * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
     */
    private function readExecl(): array
    {
        // 接收用户上传的模板文件
        $file = request()->file('file');
        empty($file) && throwError('很抱歉，您没有上传模板文件');
        // 写入到本地临时目录
        $path = FileLocal::writeFile($file, 'batch-goods', self::$storeId);
        // 读取excel数据
        $original = ReadExecl::load($path, 0, 21);
        // 去除标题数据
        unset($original[1]);
        // 过滤空行和无效的内容
        foreach ($original as $key => $row) {
            if (!is_numeric($row['A']) || empty($row['A'])) {
                unset($original[$key]);
            }
        }
        return $original;
    }

    /**
     * 删除记录
     * @return bool
     */
    public function setDelete(): bool
    {
        if ($this['status'] == GoodsImportStatusEnum::NORMAL) {
            $this->error = '很抱歉，当前任务没有结束不能删除';
            return false;
        }
        return $this->save(['is_delete' => 1]);
    }
}